package br.inf.mfpo.fra4and.activities;

import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.AsyncQueryHandler;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import br.inf.mfpo.fra4and.R;
import br.inf.mfpo.fra4and.widgets.taptwo.FlowIndicator;
import br.inf.mfpo.fra4and.widgets.taptwo.TitleFlowIndicator;
import br.inf.mfpo.fra4and.widgets.taptwo.TitleProvider;
import br.inf.mfpo.fra4and.widgets.taptwo.ViewFlow;

public abstract class Fra4AndDetailsListActivity<T extends SimpleCursorAdapter> extends Fra4AndActivity {

	private static final String SELECTED = "VIEWFLOW_SELECTED_POSITION";

	public static final int MENU_ITEM_EDIT = Menu.FIRST;
	public static final int MENU_ITEM_MOVE = Menu.FIRST + 1;
	public static final int MENU_ITEM_DELETE = Menu.FIRST + 2;
	public static final int MENU_ITEM_INSERT = Menu.FIRST + 3;

	/**
	 * Implementa o adaptador para lista
	 * 
	 * @return
	 */
	protected abstract T buildListAdapter();

	/**
	 * Carrega campos a partir do cursor
	 * 
	 * @param cursor
	 */
	protected abstract void populateFields(Cursor cursor);

	/**
	 * Define o título do menu de contexto da lista
	 * 
	 * @param cursor
	 * @return
	 */
	protected abstract String getContextMenuTitle(Cursor cursor);

	/**
	 * Cria e recupera o layout dos detalhes
	 * 
	 * @return
	 */
	protected abstract View getDetailsLayout();

	/**
	 * Define o título dos detalhes
	 * 
	 * @return
	 */
	protected abstract String getDetailsTitle();

	/**
	 * Define o título da lista
	 * 
	 * @return
	 */
	protected abstract String getListTitle();

	/**
	 * Define a projeção do cursor que alimentará os detalhes
	 * 
	 * @return
	 */
	protected abstract String[] getDetailsProjection();

	/**
	 * Define a projeção do cursor que alimentará a lista
	 * 
	 * @return
	 */
	protected abstract String[] getListProjection();

	/**
	 * Define os critérios de seleção do elementos da lista
	 * 
	 * @return
	 */
	protected abstract String getListSelection();

	/**
	 * Define os arqumentos de filtragem da lista
	 * 
	 * @return
	 */
	protected abstract String[] getListArguments();

	/**
	 * Define o tipo MIME da lista
	 * 
	 * @return
	 */
	protected abstract Uri getListUri();

	/**
	 * Retorna se é possível apagar um item da lista
	 * 
	 * @return
	 */
	protected abstract boolean canDelete();

	/**
	 * Define a mensagem do diálogo de confirmação antes de apagar um item da
	 * lista
	 * 
	 * @param position
	 * @param selection
	 * @return
	 */
	protected abstract String getDeleteMessage(int position, Uri selection);

	/**
	 * Define a mensagem a ser exibida após a exclusão
	 * 
	 * @return
	 */
	protected abstract String getDeletedMessage();

	/**
	 * Define a mensagem de erro a ser exibida, caso a exclusão falhe
	 * 
	 * @return
	 */
	protected abstract String getDeleteErrorMessage();

	/**
	 * Define o título do diálogo de mover
	 * 
	 * @return
	 */
	protected abstract String getMoveDialogTitle();

	/**
	 * O que fazer ao clicar no item do diálogo de mover
	 * 
	 * @param selection
	 * @param newId
	 * @param position
	 */
	protected abstract void onMoveClickDialog(Uri selection, long newId, int position);

	private ListView list;
	private Cursor cursor;
	private ViewFlow viewFlow;
	private int selected = 0;
	private TextView header;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.viewflow_content);
		setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);

		final Uri data = getIntent().getData();
		if (data != null) {
			cursor = managedQuery(data, getDetailsProjection(), null, null, null);
		}

		viewFlow = (ViewFlow) findViewById(R.id.viewflow);
		if (viewFlow == null) {
			throw new RuntimeException("Your content must have a ViewFlow");
		}
		FlowIndicator flowIndicator = (FlowIndicator) findViewById(R.id.indicator);
		if (flowIndicator != null) {
			if (titleProvider != null && flowIndicator instanceof TitleFlowIndicator) {
				((TitleFlowIndicator) flowIndicator).setTitleProvider(titleProvider);
			}

			viewFlow.setFlowIndicator(flowIndicator);
		}

		viewFlow.setOnViewSwitchListener(viewSwitchListener);
		viewFlow.setAdapter(viewFlowAdapter);
	}

	@Override
	protected void onResume() {
		super.onResume();
		viewFlow.setSelection(getSelected());
		final Cursor c = getCursor();
		if (c != null && c.moveToFirst()) {
			populateFields(c);
		}
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		outState.putInt(SELECTED, getSelected());
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		super.onRestoreInstanceState(savedInstanceState);
		if (savedInstanceState != null) {
			setSelected(savedInstanceState.getInt(SELECTED));
		}
	}

	/**
	 * Retorna o índice da tela selecionada (0 - detalhes; 1 - lista)
	 * 
	 * @return
	 */
	protected int getSelected() {
		return selected;
	}

	protected void setSelected(int selected) {
		this.selected = selected;
	}

	/**
	 * Retorna a lista
	 * 
	 * @return
	 */
	protected ListView getList() {
		return list;
	}

	/**
	 * Retorna o cursor dos detalhes
	 * 
	 * @return
	 */
	protected Cursor getCursor() {
		return cursor;
	}

	protected void setCursor(Cursor cursor) {
		this.cursor = cursor;
	}

	/**
	 * Retorna a consulta assíncrona usada para preencher a lista
	 * 
	 * @return
	 */
	protected ListAsyncQuery getAsyncQuery() {
		return new ListAsyncQuery(this);
	}

	private TextView getHeader() {
		if (header == null) {
			header = (TextView) findViewById(R.id.header);
		}

		return header;
	}

	/**
	 * Retorna o valor do cabeçalho
	 * 
	 * @param value
	 */
	protected void setHeaderValue(String value) {
		getHeader().setText(value);
	}

	private class ListAsyncQuery extends AsyncQueryHandler {
		private ProgressDialog pgdLoading;

		public ListAsyncQuery(Context context) {
			super(context.getContentResolver());
			pgdLoading = new ProgressDialog(context);
			pgdLoading.setIndeterminate(true);
			pgdLoading.setMessage(getString(R.string.loading));
		}

		@Override
		public void startQuery(int token, Object cookie, Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy) {
			pgdLoading.show();
			super.startQuery(token, cookie, uri, projection, selection, selectionArgs, orderBy);
		}

		@Override
		protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
			SimpleCursorAdapter adapter = (SimpleCursorAdapter) getList().getAdapter();
			adapter.changeCursor(cursor);
			adapter.notifyDataSetChanged();
			if (pgdLoading.isShowing()) {
				pgdLoading.dismiss();
			}
			super.onQueryComplete(token, cookie, cursor);
		}
	}

	private OnItemClickListener onItemClickListListener = new OnItemClickListener() {
		@Override
		public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
			onListItemClick(parent, view, position, id);
		}
	};

	public void onListItemClick(AdapterView<?> parent, View view, int position, long id) {
		Uri uri = ContentUris.withAppendedId(getListUri(), id);
		startActivity(new Intent(Intent.ACTION_VIEW, uri));
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);

		Intent intent = new Intent(null, getIntent().getData());
		intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
		menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0, new ComponentName(this, this.getClass()), null, intent, 0, null);

		return true;
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		super.onPrepareOptionsMenu(menu);

		final Uri data = getIntent().getData();
		final int COUNT = 2;

		final int selectedView = getSelected();

		Intent iMenu = new Intent();
		iMenu.addCategory(Intent.CATEGORY_ALTERNATIVE);

		Intent iEdit = new Intent(Intent.ACTION_EDIT, data);
		Intent iAdd = new Intent(Intent.ACTION_INSERT, getListUri());
		String key = data.getPathSegments().get(0);
		String value = data.getPathSegments().get(1);
		iAdd.putExtra(key, value);

		Intent[] specifics = new Intent[COUNT];
		MenuItem[] itens = new MenuItem[COUNT];

		specifics[0] = iEdit;
		specifics[1] = iAdd;

		menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0, this.getComponentName(), specifics, iMenu, 0, itens);

		if (itens[0] != null) {
			itens[0].setShortcut('3', 'e');
			if (selectedView == 0) {
				itens[0].setVisible(true);
			} else if (selectedView == 1) {
				itens[0].setVisible(false);
			}
		}

		if (itens[1] != null) {
			itens[1].setShortcut('2', 'a');
			if (selectedView == 0) {
				itens[1].setVisible(false);
			} else if (selectedView == 1) {
				itens[1].setVisible(true);
			}
		}

		if (itens[0] == null && itens[1] == null) {
			menu.removeGroup(Menu.CATEGORY_ALTERNATIVE);
		}

		return true;
	}

	@Override
	public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {

		AdapterView.AdapterContextMenuInfo info;
		try {
			info = (AdapterView.AdapterContextMenuInfo) menuInfo;
		} catch (ClassCastException e) {
			Log.e(getClass().getSimpleName(), "bad menuInfo", e);
			return;
		}

		Cursor cursor = (Cursor) getList().getAdapter().getItem(info.position);
		if (cursor == null) {
			return;
		}

		menu.setHeaderTitle(getContextMenuTitle(cursor));

		MenuItem mnuEdit = menu.add(0, MENU_ITEM_EDIT, 0, R.string.menu_edit);
		mnuEdit.setIcon(android.R.drawable.ic_menu_edit);

		MenuItem mnuMove = menu.add(0, MENU_ITEM_MOVE, 0, R.string.menu_move);
		mnuMove.setIcon(android.R.drawable.ic_menu_set_as);

		MenuItem mnuDelete = menu.add(0, MENU_ITEM_DELETE, 1, R.string.menu_delete);
		mnuDelete.setIcon(android.R.drawable.ic_menu_delete);
	}

	@Override
	public boolean onContextItemSelected(MenuItem item) {
		AdapterView.AdapterContextMenuInfo info;
		try {
			info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
		} catch (ClassCastException e) {
			Log.e(getClass().getSimpleName(), "bad menuInfo", e);
			return false;
		}

		final long id = info.id;
		final int position = info.position;
		final Uri selection = ContentUris.withAppendedId(getListUri(), id);

		switch (item.getItemId()) {
		case MENU_ITEM_DELETE:
			delete(position, selection);
			return true;
		case MENU_ITEM_EDIT:
			Intent intent = new Intent(Intent.ACTION_EDIT, selection);
			startActivity(intent);
			return true;
		case MENU_ITEM_MOVE:
			showMoveDialog(selection);
			return true;
		}

		return false;
	}

	protected ListAdapter getMoveDialogAdapter() {
		final Uri data = getIntent().getData();
		final Uri qUri = Uri.parse("content://" + data.getAuthority() + "/" + data.getPathSegments().get(0));
		final String[] sKey = { data.getPathSegments().get(1) };

		final String[] from = { getDetailsProjection()[1] };
		final int[] to = { android.R.id.text1 };
		final String where = getDetailsProjection()[0] + " <> ?";

		Cursor moveTo = managedQuery(qUri, getDetailsProjection(), where, sKey, null);
		SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.select_dialog_singlechoice, moveTo, from, to);

		return adapter;
	}

	protected void showMoveDialog(final Uri selection) {
		final ListAdapter adapter = getMoveDialogAdapter();
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setTitle(getMoveDialogTitle());
		builder.setSingleChoiceItems(adapter, -1, new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int position) {
				long newId = adapter.getItemId(position);
				onMoveClickDialog(selection, newId, position);
				dialog.dismiss();
			}
		});

		builder.create().show();
	}

	private ViewFlow.ViewSwitchListener viewSwitchListener = new ViewFlow.ViewSwitchListener() {
		@Override
		public void onSwitched(View view, int position) {
			if (position == 1) {
				getAsyncQuery().startQuery(0, null, getListUri(), getListProjection(), getListSelection(), getListArguments(), null);
			}
			setSelected(position);
		}
	};

	private Adapter viewFlowAdapter = new BaseAdapter() {
		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			if (convertView == null)
				if (position == 0) {
					convertView = getDetailsLayout();
				} else {
					convertView = getListLayout();
				}

			return convertView;
		}

		@Override
		public long getItemId(int position) {
			return position == 0 ? getDetailsLayout().getId() : getListLayout().getId();
		}

		@Override
		public Object getItem(int position) {
			return position == 0 ? getDetailsLayout() : getListLayout();
		}

		@Override
		public int getCount() {
			return 2;
		}
	};

	private TitleProvider titleProvider = new TitleProvider() {
		@Override
		public String getTitle(int position) {
			return position == 0 ? getDetailsTitle() : getListTitle();
		}
	};

	/**
	 * Retorna o layout da lista
	 * 
	 * @return
	 */
	protected View getListLayout() {
		View listLayout = View.inflate(this, R.layout.list_content, null);
		list = (ListView) listLayout.findViewById(android.R.id.list);
		if (list == null) {
			throw new RuntimeException("Your content must have a ListView whose id attribute is 'android.R.id.list'");
		}
		View emptyView = listLayout.findViewById(android.R.id.empty);
		if (emptyView != null)
			list.setEmptyView(emptyView);
		list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
		list.setOnCreateContextMenuListener(this);
		list.setAdapter(buildListAdapter());
		list.setOnItemClickListener(onItemClickListListener);

		return listLayout;
	}

	/**
	 * Apaga o item selecionado
	 * 
	 * @param selection
	 */
	protected void delete(Uri selection) {
		getContentResolver().delete(selection, null, null);
	}

	/**
	 * Apaga o item selecionado a partir de posição em uma adaptador
	 * 
	 * @param position
	 * @param selection
	 */
	protected void delete(final int position, final Uri selection) {
		AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
		dialogBuilder.setTitle(android.R.string.dialog_alert_title);
		dialogBuilder.setMessage(getDeleteMessage(position, selection));
		dialogBuilder.setIcon(android.R.drawable.ic_dialog_alert);
		dialogBuilder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				onDoDeleteClick(position, selection);
			}
		});
		dialogBuilder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				onCancelDeleteClick();
			}
		});
		dialogBuilder.create().show();
	}

	protected void onDoDeleteClick(int position, Uri selection) {
		try {
			getContentResolver().delete(selection, null, null);
			showNotification(getDeletedMessage());
			populateFields(getCursor());
		} catch (Exception e) {
			showDeleteErrorDialog(e);
		}

	}

	protected void onCancelDeleteClick() {
		showNotification(br.inf.mfpo.fra4and.R.string.canceled);
	}

	protected void showDeleteErrorDialog(Exception e) {
		AlertDialog.Builder errorDialogBuilder = new AlertDialog.Builder(this);
		errorDialogBuilder.setTitle(android.R.string.dialog_alert_title);
		errorDialogBuilder.setMessage(getDeleteErrorMessage());
		errorDialogBuilder.setIcon(android.R.drawable.ic_dialog_alert);
		errorDialogBuilder.setNeutralButton(android.R.string.ok, null);

		if (e != null) {
			Log.d(getClass().getSimpleName(), e.getLocalizedMessage());
		}

		errorDialogBuilder.create().show();
	}

}
